module dgl.graphics.Model;

import tango.text.convert.Integer;

import derelict.opengl.gl;

import dgl.GameTime;
import dgl.Matrix;
import dgl.Vector3;
import dgl.graphics.ModelMesh;
import dgl.BoundingBox;
import dgl.BoundingSphere;
import dgl.graphics.IDrawable;
import dgl.graphics.IUpdateable;
import dgl.graphics.LoadHelper;
import dgl.graphics.MeshContainer;

class Model : IDrawable
{
	private MeshContainer[] m_meshContainers;
	
	private BoundingBox m_aabb;
	
	private BoundingSphere m_boundingSphere;
	
	private Matrix m_matWorld;
	
	public this()
	{
		m_meshContainers = null;
		m_matWorld = Matrix.identity;
	}
	
	public MeshContainer[] meshContainers()
	{
		return m_meshContainers;
	}
	
	public BoundingBox aabb()
	{
		this.calcAABB();
		return m_aabb;
	}
	
	public BoundingSphere boundingSphere()
	{
		this.calcBoundingSphere();
		return m_boundingSphere;
	}
	
	public Matrix matWorld()
	{
		return m_matWorld;
	}
	
	public void matWorld( Matrix matWorld )
	{
		m_matWorld = matWorld;
		foreach( container; m_meshContainers )
		{
			container.matWorld = matWorld;
		}
	}

	public static Model loadFromFile( char[] filename )
	{
		LoadHelper loadHelper = new LoadHelper( filename );
		
		Model ret = new Model();
		
		int meshes = 0;
		char[] data;
		if( loadHelper.readLine( data ) == "Meshes" )
		{
			meshes = toInt( data );
			ret.m_meshContainers = new MeshContainer[meshes];
			for( int i = 0; i < meshes; i++ )
			{
				ret.m_meshContainers[i] = MeshContainer.loadFromFile( loadHelper, ret );
			}
		}
		
		foreach( meshContainer; ret.m_meshContainers )
		{
			if( meshContainer.parentName is null )
			{
				meshContainer.parent = null;
				break;
			}
			foreach( parentContainer; ret.m_meshContainers )
			{
				if( meshContainer.parentName == parentContainer.name )
				{
					meshContainer.parent = parentContainer;
					break;
				}
			}
		}
		
		foreach( meshContainer; ret.m_meshContainers )
		{
			meshContainer.calculateAbsoluteTransform();
		}
		
		ret.calcAABB();
		ret.calcBoundingSphere();
		
		return ret;
	}
	
	private void calcBoundingSphere()
	{
		float maxDistance = float.min;
		Vector3 center = Vector3.zero;
		foreach( container1; m_meshContainers )
		{
			foreach( container2; m_meshContainers )
			{
				Vector3 center1 = container1.boundingSphere.center;
				Vector3 center2 = container2.boundingSphere.center;
				float dist = Vector3.distance( center1, center2 );
				dist += container1.boundingSphere.radius + container2.boundingSphere.radius;
				if( dist > maxDistance )
				{
					maxDistance = dist;
					center = ( center1 + center2 ) / 2;
				}
			}
		}
		m_boundingSphere = BoundingSphere( center, maxDistance / 2 );
	}
	
	private void calcAABB()
	{
		Vector3 min = Vector3( float.max, float.max, float.max );
		Vector3 max = Vector3( -float.max, -float.max, -float.max );
		
		foreach( meshContainer; m_meshContainers )
		{
			Vector3 pos = meshContainer.aabb.max;
			if( pos.x > max.x )
				max.x = pos.x;
			if( pos.y > max.y )
				max.y = pos.y;
			if( pos.z > max.z )
				max.z = pos.z;
			pos = meshContainer.aabb.min;
			if( pos.x < min.x )
				min.x = pos.x;
			if( pos.y < min.y )
				min.y = pos.y;
			if( pos.z < min.z )
				min.z = pos.z;
		}

		m_aabb = BoundingBox( min, max );
	}
	
	public void draw( GameTime gameTime )
	{
		foreach( container; m_meshContainers )
		{
			container.draw( gameTime );
		}
	}
}